package icraf.carbon.gui;

import icraf.carbon.core.LandCover;
import icraf.carbon.core.LandCoverList;
import icraf.carbon.core.Project;
import icraf.carbon.gui.MenuFactory.MenuProperty;
import icraf.common.gui.MenuGenerator.IconModel;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.ListModel;
import javax.swing.table.TableModel;

public class LCClipboardPanel extends JPanel {
	private boolean isImport;

	public class Listener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			if (e.getSource().equals(cbHeaderLabel)) {
				if (cbHeaderLabel.isSelected()) {
					cbHeaderIndex.setSelected(false);
				}
				updateTable();
			} else if (e.getSource().equals(cbHeaderIndex)) {
				if (cbHeaderIndex.isSelected()) {
					cbHeaderLabel.setSelected(false);
				}
				updateTable();
			} else if (e.getSource().equals(importB)) {
				isImport = true;
				dialogWindow.setVisible(false);
			} else if (e.getSource().equals(cancelB)) {
				isImport = false;
				dialogWindow.setVisible(false);
			} 

		}

	}

	class SheetTableModel extends MatrixTableModel {
		private static final long serialVersionUID = 8654615864074536443L;

		private boolean editable = true;
		private NumberRowHeader numberRowHeaderModel = new NumberRowHeader(this);
		private String[][] data = new String[1][1];

		public SheetTableModel() {
			super();
		}

		@Override
		public Class<?> getColumnClass(int columnIndex) {
			return String.class;
		}

		@Override
		public int getColumnCount() {
			if (data != null)
				return data[0].length;
			return 0;
		}

		@Override
		public String getColumnName(int col) {
			return String.valueOf(col + 1);

		}

		@Override
		public int getRowCount() {
			if (data != null)
				return data.length;
			return 0;
		}

		@Override
		public ListModel<Object> getRowHeaderModel() {
			return numberRowHeaderModel;
		}

		@Override
		public Object getValueAt(int row, int column) {
			if (data == null)
				return null;
			if (row >= 0 && row < data.length && column >= 0
					&& column < data[row].length)
				return data[row][column];
			return null;
		}

		@Override
		public boolean isCellEditable(int row, int col) {
			return editable;
		}

		@Override
		public void setValueAt(Object value, int row, int col) {

			if (row >= 0 && row < data.length && col >= 0
					&& col < data[0].length)
				data[row][col] = value.toString();
			fireTableCellUpdated(row, col);
		}

		public String[][] getData() {
			return data;
		}

		public void setData(String[][] data) {
			this.data = data;
			fireTableStructureChanged();
		}

	}

	class LCClipboardTableModel extends MatrixTableModel implements
			LandCoverMatrix {
		private static final long serialVersionUID = 8654615864074536443L;

		private boolean editable = true;
		private DecimalFormat formatter = (DecimalFormat) NumberFormat
				.getNumberInstance();
		private LCRowHeaderModel lcRowHeaderModel = new LCRowHeaderModel(this);
		private Project project;

		private HashMap<LandCover, HashMap<LandCover, Double>> valueLC = new HashMap<LandCover, HashMap<LandCover, Double>>();

		public LCClipboardTableModel() {
			super();
		}

		@Override
		public Class<?> getColumnClass(int columnIndex) {
			return Double.class;
		}

		@Override
		public int getColumnCount() {
			if (project != null) {
				return project.getLandCoverList().size();
			}
			return 0;
		}

		@Override
		public String getColumnName(int col) {
			if (project == null)
				return "";
			return project.getLandCoverList().get(col).getLabel();

		}

		public Project getProject() {
			return project;
		}

		@Override
		public int getRowCount() {
			if (project == null)
				return 0;
			return project.getLandCoverList().size();
		}

		@Override
		public ListModel<Object> getRowHeaderModel() {
			return lcRowHeaderModel;
		}

		@Override
		public Object getValueAt(int row, int column) {
			if (project == null)
				return null;

			LandCoverList l = project.getLandCoverList();
			LandCover lcRow = l.get(row);
			LandCover lcCol = l.get(column);

			double val = getValue(lcRow, lcCol);

			if (Double.isNaN(val))
				return null;
			return val;
		}

		@Override
		public boolean isCellEditable(int row, int col) {
			return editable;
		}

		public void setProject(Project p) {
			this.project = p;
			lcRowHeaderModel.setProject(p);
			fireTableStructureChanged();
		}

		@Override
		public void setValueAt(Object value, int row, int col) {
			if (value == null)
				return;

			double d = 0;
			if (value instanceof String) {
				String strVal = (String) value;
				strVal = strVal.replaceAll( "[^\\d.,-Ee]", "" );
				try {
					d = formatter.parse(strVal.trim()).doubleValue();
				} catch (Exception e) {
					return;
				}
			} else {
				d = ((Double) value).doubleValue();
			}
			LandCoverList l = project.getLandCoverList();
			LandCover lcRow;
			LandCover lcCol;

			lcRow = l.get(row);
			lcCol = l.get(col);

			setValue(d, lcRow, lcCol);
			fireTableCellUpdated(row, col);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see icraf.carbon.gui.LandCoverMatrix#setValue(double,
		 * icraf.carbon.core.LandCover, icraf.carbon.core.LandCover)
		 */
		@Override
		public void setValue(double d, LandCover lc1, LandCover lc2) {
			HashMap<LandCover, HashMap<LandCover, Double>> data = valueLC;

			HashMap<LandCover, Double> lc1List = data.get(lc1);
			if (lc1List == null) {
				lc1List = new HashMap<LandCover, Double>();
			}
			lc1List.put(lc2, d);
			data.put(lc1, lc1List);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * icraf.carbon.gui.LandCoverMatrix#getValue(icraf.carbon.core.LandCover
		 * , icraf.carbon.core.LandCover)
		 */
		@Override
		public double getValue(LandCover lc1, LandCover lc2) {
			HashMap<LandCover, HashMap<LandCover, Double>> data = valueLC;
			if ((lc1 == null) || (lc2 == null)) {
				return Double.NaN;
			}
			HashMap<LandCover, Double> lc1List = data.get(lc1);
			if (lc1List == null)
				return Double.NaN;
			if (lc1List.get(lc2) == null)
				return Double.NaN;
			double d = lc1List.get(lc2);
			return d;
		}

		public void clearData() {
			valueLC.clear();
		}

	}

	private static final long serialVersionUID = -6267507992297896197L;
	private static final String TABLE_LC_MODEL = "LC",
			TABLE_SHEET_MODEL = "Sheet";
	private MatrixPanel clipboardTablePanel;
	private MatrixPanel clipboardSheetTablePanel;
	private Clipboard system;
	private int nColumn = 0;
	private DecimalFormat formatter = (DecimalFormat) NumberFormat
			.getNumberInstance();
	private LCClipboardTableModel lcTableModel = new LCClipboardTableModel();
	private SheetTableModel sheetTableModel = new SheetTableModel();
	private JCheckBox cbHeaderLabel = new JCheckBox();
	private JCheckBox cbHeaderIndex = new JCheckBox();
	private CardLayout card = new CardLayout();
	private String[][] data;
	private JPanel mainPanel = new JPanel();
	private Listener listener = new Listener();
	public ArrayList<String> columnError = new ArrayList<String>();
	public ArrayList<String> rowError = new ArrayList<String>();
	private JButton importB = MenuFactory.createButton(MenuProperty.INPUT_IMPORT, listener, true, IconModel.ICON16);
	private JButton cancelB = MenuFactory.createButton(MenuProperty.INPUT_CANCEL, listener, true, IconModel.ICON16);
	private JDialog dialogWindow;
	private boolean isLCClipboard = true;
	
	public LCClipboardPanel() {
		super();
		clipboardTablePanel = new MatrixPanel(lcTableModel, true, false, true,
				false, true);
		clipboardSheetTablePanel = new MatrixPanel(sheetTableModel, false,
				false, true, false, true);
		cbHeaderLabel.setSelected(true);
		cbHeaderLabel.addActionListener(listener);
		cbHeaderIndex.addActionListener(listener);
		JLabel l = new JLabel(MenuProperty.INPUT_CLIPBOARD_MATRIX.getLabel()
				+ ":");
		JPanel p = new JPanel();
		p.add(l);
		p.add(cbHeaderLabel);
		p.add(new JLabel(MenuProperty.INPUT_CLIPBOARD_HEADER.getLabel()));
		p.add(cbHeaderIndex);
		p.add(new JLabel(MenuProperty.INPUT_CLIPBOARD_HEADER_INDEX.getLabel()));

		mainPanel.setLayout(card);
		mainPanel.add(clipboardTablePanel, TABLE_LC_MODEL);
		mainPanel.add(clipboardSheetTablePanel, TABLE_SHEET_MODEL);
		
		JPanel pb = new JPanel();
		pb.add(importB);
		pb.add(cancelB);
		
		this.setLayout(new BorderLayout());
		this.add(p, BorderLayout.NORTH);
		this.add(mainPanel, BorderLayout.CENTER);
		this.add(pb, BorderLayout.SOUTH);
	}

	public void updateClipboardData() {
		system = Toolkit.getDefaultToolkit().getSystemClipboard();
		try {
			String trstring = (String) (system.getContents(this)
					.getTransferData(DataFlavor.stringFlavor));
			String lineStr[] = trstring.split("\n");
			String val[] = lineStr[0].split("\t");
			nColumn = val.length;
			data = new String[lineStr.length][nColumn];
			for (int i = 0; i < lineStr.length; i++) {
				data[i] = lineStr[i].split("\t");
			}
			updateTable();
		} catch (Exception ex) {
			// unsupported clipboard data
		}
	}

	public void updateTable() {
		String[] colH = data[0];
		int nCol = colH.length;
		int nRow = data.length;
		columnError.clear();
		rowError.clear();

		if (cbHeaderLabel.isSelected()) {
			isLCClipboard = true;
			lcTableModel.clearData();
			card.show(mainPanel, TABLE_LC_MODEL);
			LandCoverList l = lcTableModel.getProject().getLandCoverList();
			LandCover[] lcColH = new LandCover[nCol];
			for (int i = 1; i < nCol; i++) {
				lcColH[i] = l.findLandCover(colH[i]);
				if (lcColH[i] == null)
					columnError.add(colH[i]);
			}

			LandCover[] lcRowH = new LandCover[nRow];
			for (int i = 1; i < nRow; i++) {
				lcRowH[i] = l.findLandCover(data[i][0]);
				if (lcRowH[i] == null)
					rowError.add(data[i][0]);
			}

			for (int c = 1; c < nCol; c++) {
				for (int r = 1; r < nRow; r++) {
					double d;
					try {
						if(r >= data.length || c >= data[r].length) continue;
						data[r][c] = data[r][c].replaceAll( "[^\\d.,-Ee]", "" );
						d = formatter.parse(data[r][c].trim()).doubleValue();
						lcTableModel.setValue(d, lcRowH[r], lcColH[c]);
					} catch (ParseException e) {

					}

				}
			}
			lcTableModel.fireTableDataChanged();
		} else if (cbHeaderIndex.isSelected()) {
			isLCClipboard = true;
			lcTableModel.clearData();
			card.show(mainPanel, TABLE_LC_MODEL);
			LandCoverList l = lcTableModel.getProject().getLandCoverList();
			LandCover[] lcColH = new LandCover[nCol];
			for (int i = 1; i < nCol; i++) {
				int idx;
				try {
					idx = formatter.parse(colH[i].trim()).intValue();

					if (idx > 0 && idx <= l.size()) {
						lcColH[i] = l.get(idx-1);
					} else {
						columnError.add(colH[i]);
					}
				} catch (ParseException e) {
					columnError.add(colH[i]);
				}
			}
			LandCover[] lcRowH = new LandCover[nRow];
			for (int i = 1; i < nRow; i++) {
				int idx;
				try {
					idx = formatter.parse(data[i][0].trim()).intValue();
					if (idx > 0 && idx <= l.size()) {
						lcRowH[i] = l.get(idx-1);
					} else {
						rowError.add(data[i][0]);
					}
				} catch (ParseException e) {
					rowError.add(data[i][0]);
				}

			}
			for (int c = 1; c < nCol; c++) {
				for (int r = 1; r < nRow; r++) {
					if(r >= data.length || c >= data[r].length) continue;
					double d;
					try {
						data[r][c] = data[r][c].replaceAll( "[^\\d.,-Ee]", "" );
						d = formatter.parse(data[r][c].trim()).doubleValue();
						lcTableModel.setValue(d, lcRowH[r], lcColH[c]);
					} catch (ParseException e) {

					}

				}
			}
			lcTableModel.fireTableDataChanged();
		} else {
			isLCClipboard = false;
			card.show(mainPanel, TABLE_SHEET_MODEL);
			sheetTableModel.setData(data);
			clipboardSheetTablePanel.getRowHeader().validate();
		}
	}

	public void setProject(Project p) {
		lcTableModel.setProject(p);
	}

	public LandCoverMatrix getLandCoverMatrix() {
		return lcTableModel;
	}

	public String[][] getData() {
		return data;
	}
	
	public JDialog createDialog(Window owner, ModalityType modalityType) {
		isImport = false;
		JDialog dialog = new JDialog(owner, modalityType);
		dialog.getContentPane().add(this);
		dialog.setTitle(MenuProperty.LIST_IMPORT_CLIPBOARD.getLabel());
		dialog.setIconImage(MenuProperty.LIST_IMPORT_CLIPBOARD.getIcon(IconModel.ICON16).getImage());
		dialog.pack();
		dialogWindow = dialog;
		return dialog;
	}

	public boolean isImport() {
		return isImport;
	}

	public boolean isLCClipboard() {
		return isLCClipboard;
	}

	public TableModel getLcTableModel() {
		return lcTableModel;
	}
}